using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace Microsoft.WowAddonStudio.Lua.RuntimeService.ShellHost
{
	/// <summary>
	/// Base class for IShellHost implementations.
	/// </summary>
	public abstract partial class BaseShellHost
	{
		/// <summary>
		/// Terminates the child <see cref="System.Diagnostics.Process"/> in console.
		/// </summary>
		/// <returns>True if success, otherwise false.</returns>
		public virtual bool TerminateChildProcess()
		{
			return NativeMethods.GenerateConsoleCtrlEvent((int)CtrlEvent.CTRL_C_EVENT, processId) > 0;
		}

		/// <summary>
		/// Controls stream buffering.
		/// </summary>
		/// <returns></returns>
		protected bool SetRedirectedStreamsUnBuffered()
		{
			bool result = shellProcess != null;
			if (result)
			{
				try
				{
					var fileStream = (FileStream) shellProcess.StandardOutput.BaseStream;
					IntPtr buffer = IntPtr.Zero;
					IntPtr stream = fileStream.SafeFileHandle.DangerousGetHandle();
					stream = new IntPtr(stream.ToInt32() - 4);

					result = SetRedirectedStreamsBufferMode(stream, buffer, NativeMethods.BufferMode._IONBF, 2) && result;

					fileStream = (FileStream) shellProcess.StandardError.BaseStream;
					stream = fileStream.SafeFileHandle.DangerousGetHandle();
					result = SetRedirectedStreamsBufferMode(stream, buffer, NativeMethods.BufferMode._IONBF, 2) && result;

					return result;
				}
				catch (Exception e)
				{
					int error = Marshal.GetLastWin32Error();
					Trace.WriteLine(e);
					if (error == ERROR_INVALID_HANDLE)
						Trace.WriteLine("Win32Error: The handle is invalid.");
				}
			}
			return result;
		}

		/// <summary>
		/// Controls stream buffering and buffer size.
		/// </summary>
		/// <param name="bufferMode">Mode of buffering</param>
		/// <param name="buffer">Buffer pointer.</param>
		/// <param name="stream">Stream pointer.</param>
		/// <param name="size">The size of the buffer.</param>
		/// <returns>Returns true if successful.</returns>
		private static bool SetRedirectedStreamsBufferMode(IntPtr stream, IntPtr buffer, NativeMethods.BufferMode bufferMode, ulong size)
		{
			return NativeMethods.setvbuf(stream, buffer, (int)bufferMode, size) == 0;
		}

		#region Win32 Native functions

		private const int ERROR_INVALID_HANDLE = 6;

		/// <summary>
		/// 
		/// </summary>
		private static class NativeMethods
		{
			/// <summary>
			/// Gets the STD handle.
			/// </summary>
			/// <param name="whichHandle">The which handle.</param>
			/// <returns></returns>
			[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
			internal static extern IntPtr GetStdHandle(int whichHandle);

			/// <summary>
			/// Setvbufs the specified stream.
			/// </summary>
			/// <param name="stream">The stream.</param>
			/// <param name="buf">The buf.</param>
			/// <param name="mode">The mode.</param>
			/// <param name="size">The size.</param>
			/// <returns></returns>
			[DllImport("msvcrt.dll", SetLastError = true)]
			internal static extern int setvbuf(IntPtr stream, IntPtr buf, int mode, ulong size);

			/// <summary>
			/// Generates the console CTRL event.
			/// </summary>
			/// <param name="dwCtrlEvent">The dw CTRL event.</param>
			/// <param name="dwProcessGroupId">The dw process group id.</param>
			/// <returns></returns>
			[DllImport("kernel32.dll", SetLastError = true)]
			internal static extern int GenerateConsoleCtrlEvent(int dwCtrlEvent, int dwProcessGroupId);

			internal enum StandardHandle
			{
				STD_INPUT_HANDLE = -10,
				STD_OUTPUT_HANDLE = -11,
				STD_ERROR_HANDLE = -12
			}

			internal enum BufferMode
			{
				_IOFBF = 0x0000,
				_IOLBF = 0x0004,
				_IONBF = 0x0040
			}			
		}

		#endregion
	}
}